package session

import (
	"context"
	"gitee.com/zhendliu/sorm/clause"
	"github.com/pkg/errors"
	"reflect"
)

// Select custom input fields
func (s *Session) Select(field ...string) *Session {
	s.selectField = append(s.selectField, field...)
	return s
}

func (s *Session) Omit(field ...string) *Session {
	s.omitField = append(s.omitField, field...)
	return s
}

// First gets the 1st row
func (s *Session) First(value interface{}) error {
	s.Model(value)
	if err := s.Limit(1).Find(value); err != nil {
		return errors.Wrap(err, "get first row  error")
	}
	return nil
}

// WithContext sets context
func (s *Session) WithContext(ctx context.Context) *Session {
	s.ctx = ctx
	return s
}

// Limit adds limit condition to clause
func (s *Session) Limit(num int) *Session {
	s.clause.Set(clause.LIMIT, num)
	return s
}

// Limit adds limit condition to clause
func (s *Session) Offset(num int) *Session {
	s.clause.Set(clause.OFFSET, num)
	return s
}

// Where adds limit condition to clause
/*func (s *Session) Where(desc string, args ...interface{}) *Session {
	var vars []interface{}
	s.clause.Set(clause.WHERE, append(append(vars, desc), args...)...)
	return s
}*/

// Where adds limit condition to clause
func (s *Session) Where(desc string, args ...interface{}) *Session {
	var vars []interface{}

	vars = append(vars, desc)
	for _, arg := range args {
		if reflect.ValueOf(arg).Kind() == reflect.Ptr && reflect.ValueOf(arg).Elem().Kind() == reflect.Slice {
			slice := reflect.ValueOf(arg).Elem()
			for i := 0; i < slice.Len(); i++ {
				vars = append(vars, slice.Index(i).Interface())
			}
		} else if reflect.TypeOf(arg).Kind() == reflect.Slice {
			slice := reflect.ValueOf(arg)
			for i := 0; i < slice.Len(); i++ {
				vars = append(vars, slice.Index(i).Interface())
			}
		} else {
			vars = append(vars, arg)
		}
	}
	s.clause.Set(clause.WHERE, vars...)
	return s
}

// Or adds limit condition to clause
/*func (s *Session) Or(desc string, args ...interface{}) *Session {
	var vars []interface{}
	s.clause.Set(clause.OR, append(append(vars, desc), args...)...)
	return s
}*/

func (s *Session) Or(desc string, args ...interface{}) *Session {
	var vars []interface{}
	vars = append(vars, desc)
	for _, arg := range args {
		if reflect.ValueOf(arg).Kind() == reflect.Ptr && reflect.ValueOf(arg).Elem().Kind() == reflect.Slice {
			slice := reflect.ValueOf(arg).Elem()
			for i := 0; i < slice.Len(); i++ {
				vars = append(vars, slice.Index(i).Interface())
			}
		} else if reflect.TypeOf(arg).Kind() == reflect.Slice {
			slice := reflect.ValueOf(arg)
			for i := 0; i < slice.Len(); i++ {
				vars = append(vars, slice.Index(i).Interface())
			}
		} else {
			vars = append(vars, arg)
		}
	}
	s.clause.Set(clause.OR, vars...)
	return s
}

// INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN、CROSS JOIN,JION
type JoinType int

const (
	JOIN JoinType = iota + 1 // inner join
	INNER
	LEFT
	RIGHT
	FULL
	CROSS
)

func (j JoinType) String() string {
	switch j {
	case JOIN:
		return "JOIN"
	case INNER:
		return "INNER JOIN"
	case LEFT:
		return "LEFT JOIN"
	case RIGHT:
		return "RIGHT JOIN"
	case FULL:
		return "FULL JOIN"
	case CROSS:
		return "CROSS JOIN"
	default:
		return "JOIN"
	}
}

// Join adds join condition to clause
func (s *Session) Join(joinType JoinType, desc string, args ...interface{}) *Session {
	vars := []interface{}{joinType.String()}
	s.clause.Set(clause.JOIN, append(append(vars, desc), args...)...)
	return s
}

// OrderBy adds order by condition to clause
func (s *Session) OrderBy(desc string) *Session {
	s.clause.Set(clause.ORDERBY, desc)
	return s
}

// GroupBy adds group by condition to clause
func (s *Session) GroupBy(desc string) *Session {
	s.clause.Set(clause.GROUPBY, desc)
	return s
}

// Having adds having condition to clause
func (s *Session) Having(desc string, args ...interface{}) *Session {
	var vars []interface{}
	s.clause.Set(clause.HAVING, append(append(vars, desc), args...)...)
	return s
}
